home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK1.toast / Development Kits (Disc 1) / AsyncDriverSample1.0b4 / AsyncDriverSample.p < prev    next >
Encoding:
Text File  |  1997-05-21  |  27.8 KB  |  854 lines  |  [TEXT/CWIE]

  1. unit AysncDriverSample;
  2.  
  3. (*
  4.     File:        AysncDriverSample.p
  5.  
  6.     Contains:    A standard Macintosh device driver.
  7.  
  8.     Written by:    Quinn "The Eskimo!"
  9.  
  10.     Copyright:    © 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.     You may incorporate this sample code into your applications without
  15.     restriction, though the sample code has been provided "AS IS" and the
  16.     responsibility for its operation is 100% yours.  However, what you are
  17.     not permitted to do is to redistribute the source as "DSC Sample Code"
  18.     after having made changes. If you're going to re-distribute the source,
  19.     we require that you make it clear in the source that the code was
  20.     descended from Apple Sample Code, but that you've made changes.
  21. *)
  22.  
  23. interface
  24.  
  25.     uses
  26.         Types,
  27.         Devices,
  28.         Files;
  29.  
  30.     (* The 5 standard device drive entry points which are called by
  31.         the assembly code in AsyncDriverMain.
  32.     *)
  33.     
  34.     function DRVROpen(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  35.     function DRVRClose(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  36.     function DRVRPrime(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  37.     function DRVRControl(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  38.     function DRVRStatus(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  39.  
  40. implementation
  41.  
  42.     uses
  43.         Errors,
  44.         OSUtils,
  45.         Memory,
  46.         TextUtils,
  47.         Resources,
  48.         Events,
  49.         AppleTalk,
  50.         FSM,
  51.         DriverGestalt,
  52.         
  53.         PascalA4,
  54.         
  55.         DiskImageCore,
  56.         AsyncDriverCommon;
  57.     
  58.     (* ***** Standard Driver Declarations  ***** *)
  59.     
  60. {$PUSH}
  61. {$ALIGN MAC68K}
  62.  
  63.     (* The following are type and constant declarations required to field
  64.         standard control and status calls that block device drivers
  65.         are required to support.  The question is "Why aren't these defined
  66.         in a system interface file?"  /I don't know!/
  67.         
  68.        These are mostly documented in Technote DV 17:
  69.      
  70.          <http://devworld.apple.com/dev/technotes/dv/dv_17.html>
  71.      
  72.        We switch to 68K alignment when declaring these structures, not because
  73.         we expect this code to be compiled for PPC, but because someone
  74.         might use Metrowerk's "68K 4-byte" alignment when compiling
  75.         68K code.
  76.     *)
  77.  
  78.     (* Constants for standard control calls. *)
  79.  
  80.     const
  81.         (* Traditional control calls, as defined by Technote DV 17. *)
  82.         kKillIOControlCode            = 1;
  83.         kVerifyDiskControlCode        = 5;
  84.         kFormatDiskControlCode        = 6;
  85.         kEjectDiskControlCode        = 7;
  86.         kSetTagBufferControlCode    = 8;
  87.         kTrackCacheControlCode        = 9;
  88.         kPhysicalIconControlCode    = 21;
  89.         kMediaIconControlCode        = 22;
  90.         kDriveInfoControlCode        = 23;
  91.  
  92.         kTrackDumpControlCode        = 18244;
  93.         
  94.         (* Extra control calls defined by "Designing PCI Cards and Drivers for
  95.                 Power Macintosh Computers", p114 - 117.
  96.         *)
  97.         kSetStartupDriveControlCode     = 44;
  98.         kRegisterPartionControlCode     = 50;
  99.         kGetADriveControlCode            = 51;
  100.         kProhibitMountingControlCode     = 52;
  101.         kSetPowerModeControlCode        = 70;
  102.     
  103.     (* Constants for standard status calls. *)
  104.  
  105.     const
  106.         (* Traditional control calls, as defined by Technote DV 17. *)
  107.         kReturnFormatListStatusCode     = 6;
  108.         kDriveStatusStatusCode            = 8;
  109.     
  110.         (* Extra status calls defined by "Designing PCI Cards and Drivers for
  111.             Power Macintosh Computers", p114 - 117.
  112.         *)
  113.         kGetPartitionStatusStatusCode    = 50;
  114.         kGetPartitionInfoStatusCode        = 51;
  115.         kGetPowerModeStatusCode         = 70;
  116.         
  117.     (* Type needed for kReturnFormatListStatusCode call. *)
  118.  
  119.     type
  120.         FormatDesc =
  121.             record
  122.                 capacityInBlocks: longint;
  123.                 flagsAndOtherInfo: longint;
  124.             end;
  125.         FormatDescPtr = ^FormatDesc;
  126.     
  127.     (* Type needed for kPhysicalIconControlCode call. *)
  128.     
  129.     type
  130.         IconDescription =
  131.             record
  132.                 icon: IconType;
  133.                 description: Str255;
  134.             end;
  135.  
  136.     (* This parameter block is a copy of the standard IOParamBlock,
  137.             but with variant to allow access to all of the strange fields
  138.             used by block device drivers.
  139.     *)
  140.     
  141.     type
  142.         ExtendedParamBlock =
  143.             record
  144.                 qLink: QElemPtr;
  145.                 qType: INTEGER;
  146.                 ioTrap: INTEGER;
  147.                 ioCmdAddr: Ptr;
  148.                 ioCompletion: ProcPtr;
  149.                 ioResult: OSErr;
  150.                 ioNamePtr: StringPtr;
  151.                 ioVRefNum: INTEGER;
  152.                 ioCRefNum: integer;
  153.                 csCode: integer;
  154.                 case integer of
  155.                 
  156.                     kReturnFormatListStatusCode: (
  157.                             formatCount: integer;
  158.                             formatPoint: FormatDescPtr;
  159.                     );
  160.                     
  161.                     kDriveStatusStatusCode: (
  162.                             statusCurrentTrack: integer;
  163.                             statusFlags: signedByte;
  164.                             statusDiskInPlace: signedByte;
  165.                             statusDriveInstalled: signedByte;
  166.                             statusNumberOfSides: signedByte;
  167.                             statusDriveQElement: DrvQEl;
  168.                     );
  169.                     
  170.             end;
  171.         ExtendedParamBlockPtr = ^ExtendedParamBlock;
  172.  
  173.     (* Constants for the "disk in place" field of the drive queue element. *)
  174.  
  175.     const
  176.         kDiskNotPresent = 0;
  177.         kDiskJustInserted = 1;
  178.         kDiskHasBeenRead = 2;
  179.  
  180. {$ALIGN RESET}
  181. {$POP}
  182.  
  183.     (* ***** Global Variables **** *)
  184.  
  185.     (* We use global variables to hold the physical and media icons because
  186.         they're global to all the drives that we mount.
  187.     *)
  188.         
  189.     var
  190.         gDriverVersion : NumVersion;
  191.         gPhysicalIconInfo : IconDescription;
  192.         gMediaIcon : IconDescription;
  193.  
  194.     (* ***** Asynchronous Stuff **** *)
  195.  
  196.     function PascalCompletion(xpb : XPPParmBlkPtr; errResult : OSErr) : OSErr;
  197.         (* This routine is called by GenericCompletion in response to a
  198.             completed XPP operation.  We do the following operations:
  199.             
  200.             1.    Get the error result out of the XPP paramblock.
  201.             2.    If we get no error, we set ioActCount in the active paramblock
  202.                 on the head of our driver queue.
  203.             3.    We also adjust dCtlPosition in our DCE.
  204.         *)
  205.         var
  206.             oldA4 : longint;
  207.             dctl : DCtlPtr;
  208.             pbToComplete : ParmBlkPtr;
  209.     begin
  210.         oldA4 := SetUpA4;
  211.         if errResult = 0 then begin
  212.             errResult := xpb^.cmdResult;
  213.             if errResult = noErr then begin
  214.  
  215.                 (* Recover the dctl from the longint immediately in
  216.                     front of the XPPParamBlock in the DiskImageRecord.
  217.                 *)
  218.                 dctl := DCtlPtr( LongintPtr(ord4(xpb) - 4)^ );
  219.                 
  220.                 (* Get the paramblock to complete from the head of our driver queue. *)
  221.                 pbToComplete := ParmBlkPtr(dctl^.dCtlQHdr.qHead);
  222.                 
  223.                 (* Adjust the ioActCount and dCtlPosition fields in the paramblock we're
  224.                     about to complete.
  225.                 *)
  226.                 pbToComplete^.ioActCount := pbToComplete^.ioReqCount;
  227.                 dctl^.dCtlPosition := dctl^.dCtlPosition + pbToComplete^.ioActCount;
  228.             end; (* if *)
  229.         end; (* if *)
  230.         PascalCompletion := errResult;
  231.         oldA4 := SetA4(oldA4);
  232.     end; (* PascalCompletion *)
  233.         
  234.     procedure GenericCompletion;
  235.         (*
  236.         ; GenericCompletion
  237.         ;
  238.         ; A generic completion routine that calls through to the high-level
  239.         ; language completion routine MyPascalCompletion, which should be
  240.         ; declared as:
  241.         ;
  242.         ;   extern pascal OSErr PascalCompletion(ParamBlockRec pb,
  243.         ;                                         OSErr errResult);
  244.         ;
  245.         ; MyPascalCompletion should return 1 if there is more work to do,
  246.         ; or return <= 0 if this is the last step of this I/O request and
  247.         ; we should complete it by jumping to IODone.
  248.         ;
  249.         ; Assumed Inputs:
  250.         ;    a0.l      = pointer to the parameter block that just completed
  251.         ;    d0.w      = error result for that parameter block
  252.         ;    myDCE(a0) = param block extension holds DCE for our driver
  253.         ;
  254.         ; Assumed Outputs:
  255.         ;    preserves Pascal registers
  256.         ;    ie assumes that our caller doesn't care about D0-D2 and A0-A1
  257.         ;    when we return
  258.         *)
  259.         asm;
  260.         const
  261.             JIODone = $08FC;
  262.     begin
  263.         move.l  a0,-(sp)        (* save a0 around PascalCompletion call *)
  264.  
  265.         clr.w   -(sp)           (* ; d0:=PascalCompletion(param_block,err); *)
  266.         move.l  a0,-(sp)        (* ;       " *)
  267.         move.w  d0,-(sp)        (* ;       " *)
  268.         jsr     PascalCompletion (* ;      " *)
  269.         move.w  (sp)+,d0        (* ;       " *)
  270.  
  271.         movea.l (sp)+,a0        (* ; recover a0 (param block), doesn't set flags *)
  272.         
  273.            bgt.s   JustRTS         (* ; (d0 > 0) => we have extra work, so just return *)
  274.         
  275.           move.l  -4(a0),a1         (* ; put the DCE in a1 for sake of IODone *)
  276.           move.l  JIODone,-(sp)   (* ; put IODone on the stack and return to it *)
  277.                                 (* ; IODone will do an RTS itself, which will *)
  278.                                 (* ; return to our caller *)
  279. JustRTS:
  280.         rts
  281.     end; (* GenericCompletion *)
  282.  
  283.  
  284.     (* ***** Drive Queue Operations ***** *)
  285.  
  286.     function DriveExists (driveNum: integer): Boolean;
  287.         (* This routine returns true if the given drive number is already
  288.             being used.  The algorithm is easy: walk through the
  289.             drive queue and see whether the number matches any of the
  290.             existing drives.
  291.         *)
  292.         var
  293.             currentElement: DrvQElPtr;
  294.     begin
  295.         DriveExists := false;
  296.         currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
  297.         while currentElement <> nil do begin
  298.             if currentElement^.dQDrive = driveNum then begin
  299.                 DriveExists := true;
  300.                 leave;
  301.             end; (* if *)
  302.             currentElement := DrvQElPtr(currentElement^.qLink);
  303.         end; (* while *)
  304.     end; (* DriveExists *)
  305.  
  306.     function FindFreeDriveNumber : integer;
  307.         (* This routine finds a free drive number that we can use.
  308.             The algorithm is simple: start at 5 and keep incrementing
  309.             the number until we find one that isn't used.
  310.            This routine makes the implicit assumption that the drive
  311.             queue can never be full, ie that candidateDriveNumber will
  312.             never wrap beyond 32767.  While there's no technical
  313.             reason that this can't happen, in practical terms
  314.             it's *extremely* unlikely.
  315.         *)
  316.         var
  317.             candidateDriveNumber : integer;
  318.     begin
  319.         candidateDriveNumber := 5;            (* drive numbers 1-4 are reserved *)
  320.         while DriveExists(candidateDriveNumber) do begin
  321.             candidateDriveNumber := candidateDriveNumber + 1;
  322.         end; (* while *)
  323.         FindFreeDriveNumber := candidateDriveNumber;
  324.     end; (* FindFreeDriveNumber *)
  325.  
  326.     function DriveToDriveRecord (driverRefNum : integer; driveNum: integer; var disk : DiskImageRecordPtr): OSErr;
  327.         (* This routine finds the DiskImageRecord associated with a particular
  328.             drive number.  It simply walks the drive queue looking for
  329.             the particular drive number and returns the queue element
  330.             associated with that drive number.  Remember that DiskImageRecord
  331.             are record extensions of disk queue elements.
  332.            There are two subtleties here:
  333.          
  334.              1.    We have to subtract 4 from the address of the disk queue element
  335.                  to get the DiskImageRecordPtr because of the flags that lurk
  336.                  in front of the disk queue element.
  337.                   
  338.              2.    We only match a drive if the driver refnum matches
  339.                  along with the drive number.  This guards someone passing
  340.                  us a valid drive that isn't for us.  Of course, if someone
  341.                  has installed a drive with the same drive number as one
  342.                  of ours, the system is going to be very sick very quickly.
  343.         *)
  344.         var
  345.             currentElement: DrvQElPtr;
  346.     begin
  347.         DriveToDriveRecord := nsDrvErr;
  348.         currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
  349.         while currentElement <> nil do begin
  350.             if (currentElement^.dQDrive = driveNum) & (currentElement^.dQRefNum = driverRefNum) then begin
  351.                 disk := DiskImageRecordPtr(longint(currentElement) - 4);
  352.                 DriveToDriveRecord := noErr;
  353.                 leave;
  354.             end; (* if *)
  355.             currentElement := DrvQElPtr(currentElement^.qLink);
  356.         end; (* while *)
  357.     end; (* DriveToDriveRecord *)
  358.  
  359.     function VolumeMountedOnDrive(driveNum : integer) : Boolean;
  360.         (* This function returns true if there is a volume in the ejected state that
  361.             was ejected from the given driven.  It does this by walking the system
  362.             VCB list.  See IM: Files, p2-80 for a description of the meanings of
  363.             vcbDrvNum and vcbDRefNum.
  364.         *)
  365.         var
  366.             currentVolume : VCBPtr;
  367.     begin
  368.         VolumeMountedOnDrive := false;
  369.         currentVolume := VCBPtr(GetVCBQHdr^.qHead);
  370.         while currentVolume <> nil do begin
  371.             if (currentVolume^.vcbDrvNum = 0) & (currentVolume^.vcbDRefNum = driveNum) then begin
  372.                 VolumeMountedOnDrive := true;
  373.                 leave;
  374.             end; (* if *)
  375.             currentVolume := VCBPtr(currentVolume^.qLink);
  376.         end; (* while *)
  377.     end; (* VolumeMountedOnDrive *)
  378.     
  379.     function CountInstalledDrives(driverRefNum : integer) : integer;
  380.         (* This routine counts the number of drives that are currently
  381.             installed that are being controlled by the given driver.
  382.         *)
  383.         var
  384.             result : integer;
  385.             currentElement: DrvQElPtr;
  386.     begin
  387.         result := 0;
  388.         currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
  389.         while currentElement <> nil do begin
  390.             if currentElement^.dQRefNum = driverRefNum then begin
  391.                 result := result + 1;
  392.             end; (* if *)
  393.             currentElement := DrvQElPtr(currentElement^.qLink);
  394.         end; (* while *)
  395.         CountInstalledDrives := result;
  396.     end; (* CountInstalledDrives *)
  397.     
  398.     (* ***** Core Operations ***** *)
  399.  
  400.     function MountImage(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  401.         (* This routine is called in response to a kMountImageControlCode call.
  402.             pb point to a MountParamBlockPtr, which gives us the FSSpec of
  403.             the file to mount.
  404.         *)
  405.         var
  406.             err: OSErr;
  407.             disk : DiskImageRecordPtr;
  408.             junk: OSErr;
  409.             driveNum: integer;
  410.     begin
  411.         (* Create the core DiskImageRecord and open the AFP fork to the file. *)
  412.         err := CreateDiskImage(MountParamBlockPtr(pb)^.csParamFileToMount^, disk);
  413.         
  414.         (* Prepare the rest of the fields in the DiskImageRecord. *)
  415.         if err = noErr then begin
  416.  
  417.             (* First the four flag bytes. *)
  418.             disk^.driveFlags := signedByte($80);    (* bit 7 set => read only drive *)
  419.             disk^.diskInPlace := kDiskJustInserted;
  420.             disk^.driveInstalled := 1;
  421.             disk^.numberOfSides := -1;
  422.  
  423.             (* Now the drive queue element. *)
  424.             driveNum := FindFreeDriveNumber;
  425.             disk^.driveQElement.qType := drvQType;
  426.             disk^.driveQElement.dQDrive := driveNum;
  427.             disk^.driveQElement.dQRefNum := pb^.ioCRefNum;
  428.             disk^.driveQElement.dQFSID := 0;
  429.             disk^.driveQElement.dQDrvSz := disk^.diskSize div 512;
  430.             disk^.driveQElement.dQDrvSz2 := 0;
  431.  
  432.             (* Most of the rest of the fields are setup by CreateDiskImage.  The only
  433.                 other thing we need is to save away our DCE near the XPPParamBlock,
  434.                 so that the completion routine can get at it.
  435.             *)
  436.             disk^.driverDCE := dctl;
  437.             
  438.             (* Add the drive into the drive queue and post a suitable disk inserted event. *)
  439.             AddDrive(dctl^.dCtlRefNum, driveNum, @disk^.driveQElement);
  440.             junk := PostEvent(diskEvt, driveNum);
  441.         end; (* if *)
  442.  
  443.         MountImage := err;
  444.     end; (* MountImage *)
  445.  
  446.     procedure DoAccRun(pb: ParmBlkPtr; dctl: DCtlPtr);
  447.         (* DoAccRun is called in response to an accRun control call.  The call
  448.             is guaranteed to be operating at SystemTask time, which means we
  449.             can make synchronous calls and use the Memory Manager.
  450.            The routine walks the drive queue looking for drives that belong
  451.             to us that have been ejected and takes the appropriate action.
  452.            Note the use of SysError(dsIOCoreErr) in this routine.  This
  453.             error should never occur because it implies that the drive
  454.             queue element we just found by walking the drive queue is
  455.             no longer *in* the drive queue.  If that happens, someone
  456.             is pulling our drive queue elements out at interrupt time,
  457.             and, as far as we're concerned, the system is hosed.
  458.            I spent a lot of time pondering whether to put this error
  459.             in or just to ignore it.   In general, you should only call
  460.             SystemError when you have reason to believe that taking
  461.             down the system is the *safest* thing to do at that point,
  462.             because the system is in danger of corrupting user data.
  463.             I think this situation fits.
  464.            Note the description of dsIOCoreErr = 14 in "IM: Operating
  465.             System Utils", p2-9.
  466.         *)
  467.         var
  468.             err : OSErr;
  469.             junk : OSErr;
  470.             currentElement: DrvQElPtr;
  471.             disk : DiskImageRecordPtr;
  472.     begin
  473.         (* First clear the dNeedTime flag for our driver.  The order in which we do
  474.             this is important.  We need to do this before we start walking the
  475.             drive queue, because UnMountImage sets the flag after it marks
  476.             the drive as kDiskNotPresent.  An UnMountImage call can happen
  477.             at any time during this routine, but the routine will still
  478.             do the right thing.  If it happens before we clear the flag,
  479.             we'll clear the flag and see the kDiskNotPresent drive
  480.             in the drive queue.  If it happens after we clear the flag,
  481.             the flag will still be set when we leave this routine, so we'll
  482.             get another accRun call.
  483.            Also note that we use non-atomic operation to change the flag.
  484.             This works because we're merely setting or clearing it,
  485.             and don't care about it's previous value.
  486.         *)
  487.         
  488.         {$setc BrokenBuggyCodeWarrior:=true}
  489.         {$ifc BrokenBuggyCodeWarrior}
  490.             dctl^.dCtlFlags := band(dctl^.dCtlFlags, $DFFF);
  491.         {$elsec}
  492.             (* BNOT seems to be broken under CW11. *)
  493.             dctl^.dCtlFlags := band(dctl^.dCtlFlags, bnot(dNeedTimeMask));
  494.         {$endc}
  495.  
  496.         (* Now walk the drive queue looking for drives that belong to us
  497.             that are marked as kDiskNotPresent.
  498.         *)
  499.         currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
  500.         while currentElement <> nil do begin
  501.             if currentElement^.dQRefNum = pb^.ioRefNum then begin
  502.                 disk := DiskImageRecordPtr(longint(currentElement) - 4);
  503.                 if disk^.diskInPlace = kDiskNotPresent then begin
  504.                 
  505.                     (* We've found such a drive.  If the system still has a VCB for
  506.                         the volume that was recently ejected, then we just post
  507.                         a disk inserted event to reinsert the drive.  This
  508.                         happen when the user hits "Eject" in the Finder.
  509.                        If there is not VCB for the drive, we remove the drive
  510.                         from the drive queue and dispose it.  This is the standard
  511.                         unmount case, ie the user hits "Put Away" in the Finder".
  512.                          When we do this, we reset currentElement back to the head of
  513.                          the list, otherwise the "follow next link" code at the end of
  514.                         while loop would send us off into never-never land.
  515.                        Note that this distinction is only necessary because we've
  516.                         marked our drive as "ejectable", which is only necessary
  517.                         because we don't want VM paging off it.  *sigh*  I've
  518.                         already asked engineering for a better way of telling VM
  519.                         not to page off your drive in the future.
  520.                     *)
  521.                 
  522.                     if VolumeMountedOnDrive(disk^.driveQElement.dQDrive) then begin
  523.                         junk := PostEvent(diskEvt, disk^.driveQElement.dQDrive);
  524.                     end else begin
  525.                         if DeQueue(@disk^.driveQElement, GetDrvQHdr) = noErr then begin
  526.                             currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
  527.                         end else begin
  528.                             SysError(dsIOCoreErr);        (* See comment at start of routine. *)
  529.                         end; (* if *)
  530.                         err := DestroyDiskImage(disk);
  531.                     end; (* if *)
  532.                     
  533.                 end; (* if *)
  534.             end; (* if *)
  535.             currentElement := DrvQElPtr(currentElement^.qLink);
  536.         end; (* while *)
  537.     end; (* DoAccRun *)
  538.  
  539.     function UnMountImage(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  540.         (* UnMountImage is called in response to a kEjectDiskControlCode call.
  541.             This call may be issued synchronously or asynchronously, so
  542.             we can't make synchronous calls or use the Memory Manager.  For
  543.             this reason, we simply mark the drive as ejected and defer the
  544.             actual destruction of the drive queue element until DoAccRun time.
  545.         *)
  546.         var
  547.             err: OSErr;
  548.             disk : DiskImageRecordPtr;
  549.     begin
  550.         err := DriveToDriveRecord(pb^.ioRefNum, pb^.ioVRefNum, disk);
  551.         if err = noErr then begin
  552.             (* Simply mark the disk as no longer present, we'll clean it up in the DoAccRun routine. *)
  553.             disk^.diskInPlace := kDiskNotPresent;
  554.             
  555.             (* Now set dNeedTime so that the system will start giving us accRun calls
  556.                 as soon as possible.
  557.             *)
  558.             dctl^.dCtlDelay := 1;
  559.             dctl^.dCtlFlags := bor(dctl^.dCtlFlags, dNeedTimeMask);
  560.         end; (* if *)
  561.         UnMountImage := err;
  562.     end; (* UnMountImage *)
  563.  
  564.     (* ***** Standard Driver Entry Points ***** *)
  565.  
  566.     function DRVROpen(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  567.         (* Respond to the standard Open call.  Most drivers do a lot
  568.             more work here, such as walking a partition table and
  569.             creates drives for all the partitions in the table.  We
  570.             don't need to do this because we create drives in
  571.             response to the kMountImageControlCode call.
  572.         *)
  573.     begin
  574.         {$unused pb}
  575.         {$unused dctl}
  576.         (* DebugStr('DRVROpen'); *)
  577.         
  578.         (* Remember A4 for use in our callbacks, specifically PascalCompletion. *)
  579.         RememberA4;
  580.         
  581.         DRVROpen := InitDiskImageCore;
  582.     end; (* DRVROpen *)
  583.  
  584.     function DRVRClose(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  585.         (* Respond to the standard Close call.  We only allow closing if
  586.             we have no installed drives at the moment.
  587.         *)
  588.         var
  589.             err: OSErr;
  590.     begin
  591.         {$unused dctl}
  592.         if CountInstalledDrives(pb^.ioRefNum) = 0 then begin
  593.             err := noErr;
  594.         end else begin
  595.             err := closErr;
  596.         end; (* if *)
  597.         DRVRClose := err;
  598.     end; (* DRVRClose *)
  599.  
  600.     function DRVRPrime(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  601.         (* Respond to the standard Prime call, ie Read/Write.  The core
  602.             read functionality (this sample makes all drives read-only)
  603.             is performed by the ReadDiskImage routine.
  604.         *)
  605.         var
  606.             err: OSErr;
  607.             offset: longint;
  608.             disk : DiskImageRecordPtr;
  609.     begin
  610.         (* First find the disk we're operating on. *)
  611.         err := DriveToDriveRecord(pb^.ioRefNum, pb^.ioVRefNum, disk);
  612.         
  613.         if err = noErr then begin
  614.  
  615.             (* Now check the parameters to make sure everything is in range. *)
  616.             offset := dctl^.dCtlPosition;
  617.             if (offset < 0) or (pb^.ioReqCount < 0) or (offset + pb^.ioReqCount > disk^.diskSize) then begin
  618.                 pb^.ioActCount := 0;
  619.                 err := paramErr;
  620.             end else begin
  621.             
  622.                 (* Everything is good, lets do the read/write call. *)
  623.                 err := noErr;
  624.                 pb^.ioActCount := 0;
  625.                 if odd(pb^.ioTrap) then begin
  626.                     (* _Write *)
  627.                     err := wPrErr;
  628.                 end else begin
  629.                     (* _Read *)
  630.                     disk^.diskInPlace := kDiskHasBeenRead;
  631.                     err := ReadDiskImage(disk, pb^.ioPosOffset, pb^.ioReqCount, pb^.ioBuffer, @GenericCompletion);
  632.                 end; (* if *)
  633.                 
  634.                 (* Now adjust ioActCount and dCtlPosition.  This only happens if
  635.                     ReadDiskImage returns noErr.  However, because we called it
  636.                     asynchronously, ReadDiskImage will almost always return
  637.                     ioInProgress (ie 1).  I left this code in so that I can
  638.                     switch the driver back to synchronous operations easily.
  639.                     The real setting of these values is done in the PascalCompletion
  640.                     routine.
  641.                 *)
  642.                 if err = noErr then begin
  643.                     pb^.ioActCount := pb^.ioReqCount;
  644.                     dctl^.dCtlPosition := dctl^.dCtlPosition + pb^.ioActCount;
  645.                 end; (* if *)
  646.             end; (* if *)
  647.         end; (* if *)
  648.         DRVRPrime := err;
  649.     end; (* DRVRPrime *)
  650.  
  651.     function DRVRControl(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  652.         (* Respond to the standard Control calls. *)
  653.         var
  654.             err: OSErr;
  655.     begin
  656.         case pb^.csCode of
  657.         
  658.             (* Standard block device control calls. *)
  659.             
  660.             kKillIOControlCode:            (* It's just too hard to implement KillIO in this sample. *)
  661.                 err := -1;                (* Fortunately it's legal for me to just return an error. *)
  662.                 
  663.             kVerifyDiskControlCode: 
  664.                 err := noErr;
  665.                 
  666.             kFormatDiskControlCode: 
  667.                 err := noErr;
  668.                 
  669.             kEjectDiskControlCode: 
  670.                 err := UnMountImage(pb, dctl);
  671.                 
  672.             kSetTagBufferControlCode: 
  673.                 if pb^.ioMisc = nil then begin
  674.                     (* We're setting no tag buffer, succeed the call. *)
  675.                     err := noErr;
  676.                 end else begin
  677.                     (* We're setting a tag buffer, fail the call. *)
  678.                     err := -1;
  679.                 end; (* if *)
  680.                 
  681.             kTrackCacheControlCode: 
  682.                 err := -1;
  683.                 
  684.             kPhysicalIconControlCode:
  685.                 begin
  686.                     pb^.ioMisc := @gPhysicalIconInfo;
  687.                     err := noErr;
  688.                 end;
  689.                 
  690.             kMediaIconControlCode:
  691.                 begin
  692.                     pb^.ioMisc := @gMediaIcon;
  693.                     err := noErr;
  694.                 end;
  695.                 
  696.             kDriveInfoControlCode:
  697.                 begin
  698.                     (* See Technote DV 17 for the format of these longint. *)
  699.                     pb^.ioMisc := Ptr($00000404);
  700.                     err := noErr;
  701.                 end;
  702.                 
  703.             accRun: 
  704.                 DoAccRun(pb, dctl);
  705.                 
  706.             kTrackDumpControlCode:
  707.                 err := -1;
  708.             
  709.             kDriverConfigureCode:
  710.                 (* No driver configure calls are currently defined, but the csCode is reserved
  711.                     and drivers should not implement it.
  712.                 *)
  713.                 err := controlErr;
  714.  
  715.             (* Private control codes *)
  716.  
  717.             kMountImageControlCode:
  718.                 err := MountImage(pb, dctl);
  719.  
  720.             kSecondaryInitControlCode: 
  721.                 begin
  722.                     (* See comment in AsyncDriverCommon.p *)
  723.                     gDriverVersion := SecondaryInitParamBlockPtr(pb)^.csParamVersion;
  724.                     gMediaIcon.icon := SecondaryInitParamBlockPtr(pb)^.csParamMediaIcon;
  725.                     gMediaIcon.description := SecondaryInitParamBlockPtr(pb)^.csParamMediaDescription;
  726.                     gPhysicalIconInfo.icon := SecondaryInitParamBlockPtr(pb)^.csParamPhysicalIcon;
  727.                     gPhysicalIconInfo.description := SecondaryInitParamBlockPtr(pb)^.csParamPhysicalDescription;
  728.  
  729.                     (* It's also traditional to put the driver version into the 
  730.                             driver's queue flags.
  731.                     *)
  732.                     dctl^.dCtlQHdr.qFlags := gDriverVersion.majorRev;
  733.                     
  734.                     err := noErr;
  735.                 end;
  736.                 
  737.             otherwise
  738.                 (* It is important that you return controlErr to any control calls that
  739.                     you don't understand.
  740.                 *)
  741.                 err := controlErr;
  742.         end; (* case *)
  743.         
  744.         DRVRControl := err;
  745.     end; (* DRVRControl *)
  746.  
  747.     function DRVRStatus(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
  748.         (* Respond to the standard Status calls. *)
  749.         var
  750.             err: OSErr;
  751.             disk : DiskImageRecordPtr;
  752.             responsePtr : Ptr;
  753.     begin
  754.         {$unused dctl}
  755.         case pb^.csCode of
  756.         
  757.             kReturnFormatListStatusCode:
  758.                 begin
  759.                     err := DriveToDriveRecord(pb^.ioRefNum, pb^.ioVRefNum, disk);
  760.                     if err = noErr then begin
  761.                         if ExtendedParamBlockPtr(pb)^.formatCount > 0 then begin
  762.                             ExtendedParamBlockPtr(pb)^.formatCount := 1;
  763.                             ExtendedParamBlockPtr(pb)^.formatPoint^.capacityInBlocks := disk^.diskSize div 512;
  764.                             ExtendedParamBlockPtr(pb)^.formatPoint^.flagsAndOtherInfo := 0;
  765.                             err := noErr;
  766.                         end else begin
  767.                             err := paramErr;
  768.                         end; (* if *)
  769.                     end; (* if *)
  770.                 end;
  771.             
  772.             kDriveStatusStatusCode:
  773.                 begin
  774.                     err := DriveToDriveRecord(pb^.ioRefNum, pb^.ioVRefNum, disk);
  775.                     if err = noErr then begin
  776.                         ExtendedParamBlockPtr(pb)^.statusCurrentTrack := 0;
  777.                         ExtendedParamBlockPtr(pb)^.statusFlags := disk^.driveFlags;
  778.                         ExtendedParamBlockPtr(pb)^.statusDiskInPlace := disk^.diskInPlace;
  779.                         ExtendedParamBlockPtr(pb)^.statusDriveInstalled := disk^.driveInstalled;
  780.                         ExtendedParamBlockPtr(pb)^.statusNumberOfSides := disk^.numberOfSides;
  781.                         ExtendedParamBlockPtr(pb)^.statusDriveQElement := disk^.driveQElement;
  782.                         (* The next two lines are required to conform to the interface described
  783.                                 in Technote DV 17.  dQDrvSz maps to two bytes ("two sided" and
  784.                                 "new interface"), both of which should be -1.  dQDrvSz2 maps
  785.                                 to "soft error count".
  786.                         *)
  787.                         ExtendedParamBlockPtr(pb)^.statusDriveQElement.dQDrvSz := -1;
  788.                         ExtendedParamBlockPtr(pb)^.statusDriveQElement.dQDrvSz2 := 0;
  789.                         err := noErr;
  790.                     end; (* if *)
  791.                 end;
  792.                 
  793.             kDriverGestaltCode:
  794.                 (* For more information about DriverGestalt, see
  795.                     "Designing PCI Cards and Drivers for Power Macintosh Computers", p106.
  796.                 *)
  797.                 begin
  798.                     err := noErr;
  799.                     case UInt32(DriverGestaltParamPtr(pb)^.driverGestaltSelector) of
  800.                         UInt32(kdgVersion):
  801.                             DriverGestaltParamPtr(pb)^.driverGestaltResponse := UInt32(gDriverVersion);
  802.                         UInt32(kdgDeviceType):
  803.                             DriverGestaltParamPtr(pb)^.driverGestaltResponse := UInt32(kdgFileType);
  804.                         UInt32(kdgSync):
  805.                             DriverGestaltParamPtr(pb)^.driverGestaltResponse := 0;        (* Only high byte is meaningful. *)
  806.                         UInt32(kdgPurge):
  807.                             begin
  808.                                 responsePtr := @pb^.ioBuffer;
  809.                                 DriverGestaltPurgeResponsePtr( responsePtr )^.purgePermission := kmOkCloseOkPurge;
  810.                                 DriverGestaltPurgeResponsePtr( responsePtr )^.purgeDriverPointer := dctl^.dCtlDriver;
  811.                             end;
  812.                         UInt32(kdgEject):
  813.                             DriverGestaltParamPtr(pb)^.driverGestaltResponse := 0;        (* We always want ejects. *)
  814.                         (* We don't support the following DriveGestalt codes:
  815.                             kdgInterface:
  816.                             {  The underlying interface that the driver is using (if any)  }
  817.                             kdgBoot:
  818.                             {  value to place in PRAM for this drive (long)  }
  819.                             kdgWide:
  820.                             {  True if driver supports ioWPosOffset  }
  821.                             kdgSupportsSwitching:
  822.                             {  True if driver supports power switching  }
  823.                             kdgMin3VPower:
  824.                             {  Minimum 3.3V power consumption in microWatts  }
  825.                             kdgMin5VPower:
  826.                             {  Minimum 5V power consumption in microWatts  }
  827.                             kdgMax3VPower:
  828.                             {  Maximum 3.3V power consumption in microWatts  }
  829.                             kdgMax5VPower:
  830.                             {  Maximum 5V power consumption in microWatts  }
  831.                             kdgInHighPower:
  832.                             {  True if device is currently in high power mode  }
  833.                             kdgSupportsPowerCtl:
  834.                             {  True if driver supports following five calls  }
  835.                             kdgAPI:
  836.                             {  API support for PC Exchange  }
  837.                             kdgFlush:
  838.                             { Determine if disk driver supports flush and if it needs a flush }
  839.                         *)
  840.                     otherwise
  841.                         err := statusErr;
  842.                     end; (* case *)
  843.                 end;
  844.  
  845.             otherwise
  846.                 (* It is important that you return statusErr to any status calls that
  847.                     you don't understand.
  848.                 *)
  849.                 err := statusErr;
  850.         end; (* case *)
  851.         DRVRStatus := err;
  852.     end; (* DRVRStatus *)
  853.  
  854. end. (* AysncDriverSample *)